package com.superman.mvpframe.client.mvp;

import java.util.Map;

import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceHistoryMapper;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.superman.mvpframe.client.gin.XGinjector;
import com.superman.mvpframe.client.history.IHtml5Historian;
import com.superman.mvpframe.client.history.PopStateEvent;
import com.superman.mvpframe.client.history.PopStateHandler;
import com.superman.mvpframe.client.router.IIgnorePush;
import com.superman.mvpframe.client.router.MyPageRouterPopHelper.PopAfterCallBack;
import com.superman.mvpframe.client.router.PagePopEvent;
import com.superman.mvpframe.client.utils.Registry;
import com.superman.mvpframe.client.utils.StringUtils;

/**
 * 历史栈管理
 * 
 * @author superman
 * @version 2018年8月8日下午5:00:15
 */
public class MyPlaceHistoryHandler {

	private static native String getState()/*-{
		return $wnd.history.state;
	}-*/;

	private Place defaultPlace = Place.NOWHERE;

	private EventBus eventBus;

	private IHtml5Historian historian;

	private boolean ignore;

	private MyPlaceController placeController;

	private final PlaceHistoryMapper placeHistoryMapper;

	/**
	 * 构造方法
	 * 
	 * @author superman
	 * @version 2018年8月8日 下午5:00:26
	 * @param placeHistoryMapper
	 * @param iHtml5Historian
	 */
	public MyPlaceHistoryHandler(PlaceHistoryMapper placeHistoryMapper, IHtml5Historian iHtml5Historian) {
		this.placeHistoryMapper = placeHistoryMapper;
		historian = iHtml5Historian;
	}

	private void backHome() {

		XGinjector.INSTANCE.getPageRouterPopHelper().popHome(new PopAfterCallBack() {

			@Override
			public void onSuccess() {
				// handleCurrentHistory();

				Place place = getPlaceForToken(getState());

				// if (defaultPlace.equals(place)) {
				ignore = true;
				// }

				MyPlaceChangeEvent xPlaceChangeEvent = new MyPlaceChangeEvent();
				xPlaceChangeEvent.setPlace(place);
				xPlaceChangeEvent.setBack(true);
				eventBus.fireEvent(xPlaceChangeEvent);

			}
		});

	}

	protected HandlerRegistration bind() {

		final HandlerRegistration popHandler = historian.addPopStateHandler(new PopStateHandler() {

			@Override
			public void onPopStateEvent(PopStateEvent event) {
				if (!XGinjector.INSTANCE.getPageRouterPopHelper().isFireFromBackToPageEvent())
					onPopStateEventOccured(event.getData());
			}
		});

		final HandlerRegistration placeChangeHandler = eventBus.addHandler(MyPlaceChangeEvent.TYPE,
				new MyPlaceChangeEvent.MyPlaceChangeEventHandler() {

					@Override
					public void onPlaceChange(MyPlaceChangeEvent event) {
						onPlaceChangeEvent(event);

					}
				});
		final HandlerRegistration pagePopHandler = eventBus.addHandler(PagePopEvent.TYPE, new PagePopEvent.PagePopEventHandler() {

			@Override
			public void onPop(PagePopEvent event) {

				if (event.isBackHome()) {
					backHome();
				} else {
					if (event.getPlace() == null)
						pop(event);
					else
						popTo(event);
				}

			}
		});

		return new HandlerRegistration() {
			@Override
			public void removeHandler() {
				MyPlaceHistoryHandler.this.defaultPlace = Place.NOWHERE;
				MyPlaceHistoryHandler.this.placeController = null;
				popHandler.removeHandler();
				placeChangeHandler.removeHandler();
				pagePopHandler.removeHandler();
			}
		};

	}

	private void completePopAndChangePlace(PagePopEvent event) {

		String token = getState();
		Place place = null;
		if (!StringUtils.isEmpty(token))
			if (Registry.get(token) == null)
				place = placeHistoryMapper.getPlace(token);
			else
				place = Registry.get(token);
		else
			place = defaultPlace;
		MyPlaceChangeEvent xPlaceChangeEvent = new MyPlaceChangeEvent();
		xPlaceChangeEvent.setPlace(place);
		xPlaceChangeEvent.setBack(true);
		if (event.getContextData() != null)
			xPlaceChangeEvent.setContextData(event.getContextData());

		eventBus.fireEvent(xPlaceChangeEvent);

	}

	protected String getFullPath(String token, Map<String, Object> contextData) {

		String fullPath = Window.Location.getQueryString();

		if (contextData != null) {

			if (fullPath.indexOf("?") == -1) {
				fullPath += "?";
			} else {
				fullPath += "&";
			}

			for (String key : contextData.keySet()) {
				fullPath += key;
				fullPath += "=";
				fullPath += contextData.get(key);
				fullPath += "&";
			}
		}

		if (fullPath.lastIndexOf("&") == fullPath.length() - 1)
			fullPath = fullPath.substring(0, fullPath.length() - 1);

		fullPath += "#";
		fullPath += History.encodeHistoryToken(token);

		return fullPath.toString();
	}

	protected Place getPlaceForToken(String token) {

		Place newPlace = null;

		if (token == null || token.isEmpty() || token.equals("null")) {
			newPlace = defaultPlace;
		}

		if (newPlace == null) {

			if (Registry.get(token) == null)
				newPlace = placeHistoryMapper.getPlace(token);
			else
				newPlace = Registry.get(token);
		}

		if (newPlace == null) {
			newPlace = defaultPlace;
		}
		return newPlace;

	}

	/**
	 * 重载
	 * 
	 * @author superman
	 * @version 2018年8月8日 下午5:00:35
	 */
	public void handleCurrentHistory() {
		Place place = getPlaceForToken(History.getToken());

		// if (defaultPlace.equals(place)) {
		ignore = true;
		// }

		MyPlaceChangeEvent xPlaceChangeEvent = new MyPlaceChangeEvent();
		xPlaceChangeEvent.setPlace(place);
		xPlaceChangeEvent.setBack(false);
		eventBus.fireEvent(xPlaceChangeEvent);
	}

	protected void onPlaceChangeEvent(MyPlaceChangeEvent event) {

		if (ignore) {
			ignore = false;
			return;
		}

		Place newPlace = event.getPlace();

		if (!(newPlace instanceof IIgnorePush) && !event.isBack()) {
			String token = tokenForPlace(newPlace);
			if (event.getContextData() != null)
				pushTokenAndSetContext(token, event.getContextData());
			else
				pushToken(token);

			Registry.register(token, newPlace);
		}
	}

	protected void onPopStateEventOccured(String token) {
		Place place = getPlaceForToken(token);

		MyPlaceChangeEvent xPlaceChangeEvent = new MyPlaceChangeEvent();
		xPlaceChangeEvent.setPlace(place);
		xPlaceChangeEvent.setBack(true);

		eventBus.fireEvent(xPlaceChangeEvent);
	}

	protected void pop(final PagePopEvent event) {

		XGinjector.INSTANCE.getPageRouterPopHelper().pop(new PopAfterCallBack() {

			@Override
			public void onSuccess() {
				completePopAndChangePlace(event);

			}
		});

	}

	protected void popTo(final PagePopEvent event) {

		final ActivityAbstractPlace place = (ActivityAbstractPlace) event.getPlace();
		final String token = placeHistoryMapper.getToken(place);
		XGinjector.INSTANCE.getPageRouterPopHelper().popTo(token, new PopAfterCallBack() {

			@Override
			public void onSuccess() {
				completePopAndChangePlace(event);
			}
		});

	}

	protected void pushToken(String token) {
		pushTokenAndSetContext(token, null);
	}

	protected void pushTokenAndSetContext(String token, Map<String, Object> contextData) {
		String fullPath = getFullPath(token, contextData);

		if (getState() == null || getState().equals("") || getState().equals("null")) {
			historian.pushState(token, Window.getTitle(), fullPath);
		} else {
			if (!getState().equals(token)) {
				historian.pushState(token, Window.getTitle(), fullPath);
			}
		}

	}

	/**
	 * 注册
	 * 
	 * @author superman
	 * @version 2018年8月8日 下午5:01:07
	 * @param placeController
	 * @param eventBus
	 * @param defaultPlace
	 * @return HandlerRegistration
	 */
	public HandlerRegistration register(MyPlaceController placeController, EventBus eventBus, Place defaultPlace) {
		this.placeController = placeController;
		this.eventBus = eventBus;
		this.defaultPlace = defaultPlace;

		final HandlerRegistration bind = bind();

		return new HandlerRegistration() {
			@Override
			public void removeHandler() {

				bind.removeHandler();
			}
		};
	}

	protected void replaceToken(String token) {
		if (token.length() > 0) {
			historian.replaceState(token, Window.getTitle(), "#" + History.encodeHistoryToken(token));
		} else {
			historian.replaceState(token, Window.getTitle(), "");
		}
	}

	private String tokenForPlace(Place newPlace) {

		if (defaultPlace.equals(newPlace)) {
			return "";
		}

		String token = placeHistoryMapper.getToken(newPlace);
		if (token != null) {
			return token;
		}

		return "";
	}
}
